package com.xiaoquan.gulimall.search.thread;

import java.util.concurrent.*;

/**
 * @authro dxq
 * @date 2021年07月02日 15:42
 */
public class ThreadTest {
    public static ExecutorService executor = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main.....start.....");
        // 1、使用异步编排任务

        // 1.1、没有返回值的
//        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 2;
//            System.out.println("运行结果：" + i);
//        }, executor);
        /**
         * CompletableFuture.runAsync运行结果
         * main.....start.....
         * main.....end.....
         * 当前线程：14
         * 运行结果：5
         */

        // 1.2、有返回值的
        /**
         * 方法完成后的感知 不能修改返回的数据
         */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 0;
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executor).whenComplete((result,exception)->{
//            // 虽然能得到异常信息，但是没办法修改返回数据
//            System.out.println("异步任务成功完成了....结果是："+result+"; 异常是：" +exception);
//            // 异步任务成功完成了....结果是：5; 异常是：null
//        }).exceptionally(throwable -> {
//            // 可以感知异常，同时返回默认值
//            return 10;
//            //异步任务成功完成了....结果是：null; 异常是：java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
//        });

        /**
         * 方法执行完成后的处理 就是根据结果，或者异常执行不同的业务逻辑 得到不同的结果
        */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 4; // i = 2
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executor).handle((result,throwable)->{
//            if (result!=null){
//                return result * 2;
//            }
//            if (throwable!=null){
//                return -1;
//            }
//            return 0;
//        });

        /**
         * 线程串行化
         * 1、thenRunAsync：不能获取到上一步的执行结果，无返回值
         *      .thenRunAsync(() -> {
         *             System.out.println("任务2启动了。。。但是没有结果返回值");
         *         }, executor);
         *  结果：
         *  main.....start.....
         * 当前线程：14
         * 运行结果：2
         * main.....end.....
         * 任务2启动了。。。但是没有结果返回值
         *
         * 2、thenAcceptAsync：能接收上一步的结果，但是无返回值
         *      .thenAcceptAsync(result->{
         *             System.out.println("任务2启动了 有上一次的返回值："+result);
         *         },executor);
         * 结果：
         * main.....start.....
         * 当前线程：14
         * 运行结果：2
         * main.....end.....
         * 任务2启动了 有上一次的返回值：2
         *
         * 3、thenApplyAsync：能接收上一步的结果，还有返回结果
         *      .thenApplyAsync((result -> {
         *             System.out.println("任务2启动了" + result);
         *             return result * 10;
         *         }));
         *  结果：
         *  main.....start.....
         * 当前线程：14
         * 运行结果：2
         * 任务2启动了2
         * main.....end.....20
         */
//        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
//            System.out.println("当前线程：" + Thread.currentThread().getId());
//            int i = 10 / 4; // i = 2
//            System.out.println("运行结果：" + i);
//            return i;
//        }, executor).thenApplyAsync((result -> {
//            System.out.println("任务2启动了" + result);
//            return result * 10;
//        }));
        // void accept(T t);
        // R apply(T t, U u)

        /**
         * 两个线程都完成 合并两个异步任务
         * 结果
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * 任务2执行结束：
         * main.....end.....
         * 任务3开启  任务1、任务2 结束了 任务3就开启了
         */
        CompletableFuture<Object> future01 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务1线程：" + Thread.currentThread().getId());
            int i = 10 / 4; // i = 2
            System.out.println("任务1执行结束：");
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务2线程：" + Thread.currentThread().getId());
            try {
                Thread.sleep(10000);
                System.out.println("任务2执行结束：");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello";
        }, executor);

//        future01.runAfterBothAsync(future02,()->{
//            System.out.println("任务3开启");
//        },executor); 不能感知 前两个线程的结果
        /**
         * thenAcceptBothAsync结果
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * 任务2执行结束：
         * main.....end.....
         * 任务3开始。。。之前的结果：任务1：2、任务2：Hello
         *
         *         future01.thenAcceptBothAsync(future02,(res01,res02)->{ // 没有返回值
         *             System.out.println("任务3开始。。。之前的结果：任务1："+ res01 +"、任务2："+ res02);
         *         },executor);
         */

        /**
         * .thenCombineAsync结果 能获取前两个线程的返回值 而且自己还有返回值
         * main.....start.....
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * 任务2执行结束：
         * 任务3开启。。。结果1：2结果2：Hello
         * main.....end.....Hello2
         */
//        CompletableFuture<String> future = future01.thenCombineAsync(future02, (r1, r2) -> {
//            System.out.println("任务3开启。。。结果1：" + r1 + "结果2：" + r2);
//            String r3 = r2 + r1;
//            return r3;
//        }, executor);

        /**
         * 两个任务，只要一个执行完成，就能执行任务3
         *  runAfterEitherAsync结果 不感知任何一个线程的返回值 自己也无返回值
         *  main.....start.....
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * main.....end.....
         * 任务3开启
         * 任务2执行结束：
         *
         *
         * acceptEitherAsync 结果 可以感知结果的返回值 自己没有返回值
         * main.....start.....
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * main.....end.....
         * 任务3开启....之前的结果：2
         * 任务2执行结束：
         *
         * applyToEitherAsync 可以感知结果 并且自己也有返回值
         *  main.....start.....
         * 任务1线程：14
         * 任务1执行结束：
         * 任务2线程：15
         * 任务3开启....之前的结果：2
         * main.....end.....2Hello
         * 任务2执行结束：
         */

//        future01.runAfterEitherAsync(future02,()->{
//                System.out.println("任务3开启");
//        },executor);

//        future01.acceptEitherAsync(future02,result->{ // 两个线程的的返回值类型应该要是一样的 只接收上一步的返回值结果 不管是哪一个的
//            System.out.println("任务3开启....之前的结果：" + result);
//        },executor);

//        CompletableFuture<String> future = future01.applyToEitherAsync(future02, r -> {
//            System.out.println("任务3开启....之前的结果：" + r);
//            String r3 = r + "Hello";
//            return r3;
//        }, executor);
        CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "xiaoquan.jpg";
        });

        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性信息");
            return "黑色+256G";
        });

        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的描述信息");
            return "华为";
        });

       // CompletableFuture<Void> allOf = CompletableFuture.allOf(futureImg, futureAttr, futureDesc); // 等所有的线程都执行完
       // allOf.get();// 等待所有线程执行完成
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
        anyOf.get(); // 只要有一个线程执行完成就可以了

        System.out.println("main.....end....." );
    }

    /**
     * 初始化线程的4中方式
     * 1、继承Thread
     *      Test01 test01 = new Test01();
     *          test01.start(); 启动线程
     *
     * 2、实现Runnable接口
     *       Runnable01 runnable01 = new Runnable01();
     *         new Thread(runnable01).start();  开启一个新的线程 启动线程
     *
     * 3、实现Callable接口(jdk1.5以后才有的) + FutureTask(可以拿到返回值结果，可以处理异常)
     *        FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
     *         new Thread(futureTask).start();
     *         // 阻塞等待 等待整个线程执行完成，获取返回结果
     *         Integer integer = futureTask.get();
     *
     * 4、线程池[ExecutorService]
     *      给线程池直接提交任务
     *         XXXX.execute(new Runnable01()); --> service.execute(new Runnable01())
     *      1、Executors   public static ExecutorService service = Executors.newFixedThreadPool(10); // 创建一个固定大小的线程池
     * 1.1、Executors.newCachedThreadPoll() core是0，所有的线程都可以回收
     * 1.2、Executors.newFixedThreadPool(10) 固定大小[10]，core=max 都不可以回收  大小可自定义指定
     * 1.3、Executors.newScheduledThreadPool() 定时任务的线程池
     * 1.4、Executors.newSingleThreadExecutor() 单线程的线程池，后台从队列里面获取任务，挨个执行，每次只执行一个任务
     *
     *      2、原生 new ThreadPoolExecutor();
     * 原生的七大参数
     * 2.1、int corePoolSize[5](核心线程数[一直存在,除非(allowCoreThreadTimeOut)]):线程池，创建好以后就准本就绪的线程数量，就等待来接受异步任务去执行
     *          5个  Thread thread = new Thread();  thread.start();
     * 2.2、int maximumPoolSize(最大线程数)：线程池内的最大线程数量；控制资源
     * 2.3、long keepAliveTime(超时时间)：存活时间。如果当前的线程数量大于核心数量
     *          释放空闲的线程(maximumPoolSize-corePoolSize)。只要线程空闲大于指定的keepAliveTime;
     * 2.4、TimeUnit unit(超时时间单位)：时间单位
     * 2.5、BlockingQueue<Runnable> workQueue(阻塞队列)：如果任务有很多，就会将目前还未执行的任务放在任务队列里面。
     *          只要有线程空闲，就回去阻塞队列里面取出还未执行的新任务继续执行
     * 2.6、ThreadFactory threadFactory(线程工厂)：线程的创建工厂。
     * 2.7、RejectedExecutionHandler handler(拒绝策略)：如果队列满了，按照我们指定的拒绝策略拒绝执行任务；
     *
     *
     * 工作顺序：
     *      1)、线程池创建，准备好core数量的核心线程，准备接受任务
     *      1.1、core满了，就将再进来的任务放入阻塞队列中。空闲的core就去自己去阻塞队列中获取任务执行
     *      1.2、阻塞队列满了，就直接开新的线程执行，最大只能开到max指定的数量
     *      1.3、max满了就用RejectedExecutionHandler拒绝任务 拒绝策略有很多中 这只是其中的一种
     *      1.4、max都执行完成，有很多的空闲线程，在指定的时间keepAliveTime以后，释放max-core这些线程
     *
     *      new LinkedBlockingDeque<>(); 默认是Integer的最大值。内存不够
     *
     *      面试题：一个线程池 core 7; max(包含core) 20; queue: 50; 100并发进来怎么分配的;
     *      首先 先创建7个任务core核心线程，在将50个任务放入阻塞队列中，阻塞队列满了以后就会new 线程 达到 线程最大值max 13个
     *      达到max后 剩下的30个任务就会执行拒绝策略
     *
     *      如果不想抛弃还要执行：CallerRunsPolicy
     *
     * 区别
     *      1、2不能得到返回值，3可以获取返回值
     *      1、2、3都不能控制资源
     *      4可以控制资源，性能稳定
     */
    public  void thread(String[] args) throws ExecutionException, InterruptedException {
//        Test01 test01 = new Test01();
//        test01.start(); // 启动线程
        /**
         * Test01结果
         * Test01.....start
         * Test01.....end
         * 当前线程：14:Thread-0
         * 5
         */

//        Runnable01 runnable01 = new Runnable01();
//        new Thread(runnable01).start(); // 开启一个新的线程 启动线程
        /**
         * Runnable01结果
         * Runnable01.....start
         * Runnable01.....end
         * 当前线程：14:Thread-0
         * 5
         */

//        System.out.println("Callable01.....start");
//        FutureTask<Integer> futureTask = new FutureTask<>(new Callable01());
//        new Thread(futureTask).start();
//        // 阻塞等待 等待整个线程执行完成，获取返回结果
//        Integer integer = futureTask.get();
//        System.out.println("Callable01.....end：");
        /**
         * Callable01结果
         * Callable01.....start
         * 当前线程：14:Thread-0
         * 5
         * Callable01.....end：5
         */

        // 在以后的业务代码里，以上三种启动线程的方式都不用。 【将所有的多线程异步任务都交给线程池执行】
        // 每次有新任务都会创建一个线程 会造成资源浪费，和空间不足
//        new Thread(()-> System.out.println("hello")).start();

        // 当前系统中 线程池只有一两个，每个异步任务，提交给线程池让他自己去执行就行
        // service.execute(new Runnable01());
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,20,10,
                TimeUnit.SECONDS,null,null,null);

    }

    public static class Test01 extends Thread{
        @Override
        public void run() {
            System.out.println("当前线程："+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
        }
    }

    public static class Runnable01 implements Runnable{
        @Override
        public void run() {
            System.out.println("当前线程："+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
        }
    }

    public static class Callable01 implements Callable<Integer>{ // 有返回值
        @Override
        public Integer call() throws Exception {
            System.out.println("当前线程："+Thread.currentThread().getId()+":"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println(i);
            return i;
        }
    }
}
